package gfo

/*MatchController
*
* MatchController gets a little tricky because the form for match creation/editing asks for values that
* are recorded as attributes of a Matchship, which is the intermediary class between Player and Match. Its
* status as intermediary classes calls for additional code in both the save and update actions.
* */
class MatchController {

    static allowedMethods = [save: "POST", update: "POST", delete: "POST"]

    def index = {
        redirect(action: "list", params: params)
    }

    def list = {
        params.max = Math.min(params.max ? params.int('max') : 10, 100)
        [matchInstanceList: Match.list(params), matchInstanceTotal: Match.count()]
    }

    def create = {
        def matchInstance = new Match()
        matchInstance.properties = params
        return [matchInstance: matchInstance]
    }

    /*Action:save
    * ------------------------------------------------
    * save, interestingly enough, is the action that does most of the work when a class instance is created,
    * rather than create (I'm not entirely sure why this is). Naturally, when a new Match is created from
    * a form submission (see Match:create.gsp in views), two new Matchships must be created as well, one
    * for each player. The relevant data for both objects can be extracted from the general variable params,
    * and once both Matchships are setup, they can be added to the Match's collection of Matchships.*/
    def save = {
        //New object definitions----------------------------
        def matchInstance = new Match(params)
        def matchshipInstance1 = new Matchship()
        def matchshipInstance2 = new Matchship()
        def winner = Player.findById(params.winner.id)
        def loser = Player.findById(params.loser.id)
        def winChar = Fighter.findById(params.winnerChar?.id)
        def loseChar = Fighter.findById(params.loserChar?.id)

        //matchship Setup-----------------------------------
        matchshipInstance1.player = winner
        matchshipInstance1.character = winChar
        matchshipInstance1.winner = true
        matchshipInstance2.player = loser
        matchshipInstance1.character = loseChar
        matchshipInstance2.winner = false
        matchInstance.save()//yes, this is necessary here--not sure why.
        matchshipInstance1.match = matchInstance
        matchshipInstance2.match = matchInstance
        matchshipInstance1.save()
        matchshipInstance2.save()
        print matchInstance.properties

        //Relationship Setup--------------------------------
        matchInstance.addToPlayers(matchshipInstance1)
        matchInstance.addToPlayers(matchshipInstance2)
        winner.addToMatches(matchshipInstance1)
        if (winChar){
            winChar.addToMatches(matchshipInstance1)
        }
        loser.addToMatches(matchshipInstance2)
        if (loseChar){
            loseChar.addToMatches(matchshipInstance2)
        }
        if (matchInstance.stage){
            matchInstance.stage.addToMatches(matchInstance)
        }
        matchInstance.tournament.addToMatches(matchInstance)

        //Automatically generated logic
        if (matchInstance.save(flush: true)) {
            flash.message = "${message(code: 'default.created.message', args: [message(code: 'match.label', default: 'Match'), matchInstance.id])}"
            redirect(action: "show", id: matchInstance.id)
        }
        else {
            render(view: "create", model: [matchInstance: matchInstance])
        }
    }

    def show = {
        def tourn = Tournament.findByName("George Foreman Open XXI")
        def rr = Bracket.findByName("Semi Finals")
        def matches = Match.findAllByBracketAndTournament(rr,tourn)
        def alex = Player.findByFullName("Alex Huszagh")
        print (alex.charRoster(matches))
        def matchInstance = Match.get(params.id)
        if (!matchInstance) {
            flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'match.label', default: 'Match'), params.id])}"
            redirect(action: "list")
        }
        else {
            [matchInstance: matchInstance]
        }
    }

    def edit = {
        def matchInstance = Match.get(params.id)
        if (!matchInstance) {
            flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'match.label', default: 'Match'), params.id])}"
            redirect(action: "list")
        }
        else {
            return [matchInstance: matchInstance]
        }
    }

    /*update
    * ---------------------------------------------
    * Again, update does most of the work when a user makes changes to an Match instance, rather than edit.
    * The logic here is similar to that of create; the form requests information pertinent to both the Match
    * and the Matchship, and once it has gleaned that information, it sets up the relationship appropriately.*/
    def update = {
        def matchInstance = Match.get(params.id)
        if (matchInstance) {
            if (params.version) {
                def version = params.version.toLong()
                if (matchInstance.version > version) {
                    
                    matchInstance.errors.rejectValue("version", "default.optimistic.locking.failure", [message(code: 'match.label', default: 'Match')] as Object[], "Another user has updated this Match while you were editing")
                    render(view: "edit", model: [matchInstance: matchInstance])
                    return
                }
            }
            def winner = Player.findById(params.winner.id)
            def loser = Player.findById(params.loser.id)
            def winChar = Fighter.findById(params.winnerChar.id)
            def loseChar = Fighter.findById(params.loserChar.id)

            //matchship Setup-----------------------------------
            for (ms in matchInstance.players){
                if (ms.winner){
                    ms.player = winner
                    ms.character = winChar
                    ms.save()
                }
                else{
                    ms.player = loser
                    ms.character = loseChar
                    ms.save()
                }
            }
            matchInstance.properties = params
            matchInstance.save()//yes, this is necessary here--not sure why.
            if (!matchInstance.hasErrors() && matchInstance.save(flush: true)) {
                flash.message = "${message(code: 'default.updated.message', args: [message(code: 'match.label', default: 'Match'), matchInstance.id])}"
                redirect(action: "show", id: matchInstance.id)
            }
            else {
                render(view: "edit", model: [matchInstance: matchInstance])
            }
        }
        else {
            flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'match.label', default: 'Match'), params.id])}"
            redirect(action: "list")
        }
    }

    def delete = {
        def matchInstance = Match.get(params.id)
//        for (matchshipInstance in matchInstance.players){//doesn't work, something about cascading delete
//            if (matchshipInstance) {
//                try {
//                    matchshipInstance.delete(flush: true)
//                    flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'matchship.label', default: 'Matchship'), params.id])}"
//                    redirect(action: "list")
//                }
//                catch (org.springframework.dao.DataIntegrityViolationException e) {
//                    flash.message = "${message(code: 'default.not.deleted.message', args: [message(code: 'matchship.label', default: 'Matchship'), params.id])}"
//                    redirect(action: "show", id: params.id)
//                }
//            }
//            else {
//                flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'matchship.label', default: 'Matchship'), params.id])}"
//                redirect(action: "list")
//            }
//        }
        if (matchInstance) {
            try {
                matchInstance.delete(flush: true)
                flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'match.label', default: 'Match'), params.id])}"
                redirect(action: "list")
            }
            catch (org.springframework.dao.DataIntegrityViolationException e) {
                flash.message = "${message(code: 'default.not.deleted.message', args: [message(code: 'match.label', default: 'Match'), params.id])}"
                redirect(action: "show", id: params.id)
            }
        }
        else {
            flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'match.label', default: 'Match'), params.id])}"
            redirect(action: "list")
        }
    }

    /*specify
    * ----------------------------------------
    * specify is tricky. This is essentially the logic I'm using to implement the user's advanced search.
    * The user basically selects instances from drop-down lists to specify the criteria for the matches he
    * wants to view. In other words, when a user views his or her page, they're shown a cumulative record of
    * their entire Smash Bros career. However, if they want to narrow that data down, they can use specify
    * to see exactly what they want to. This is tricky because there won't necessarily be values for every
    * single variable--in other words, a player may want to see all the Matches he's played as a certain
    * character, but not care about what stage they were played on. In that case, the stage box returns null--
    * but groovy doesn't know that, so we can't just use GORM for this. Instead, I just start with the entire
    * list of Matches ever, and repeatedly narrow it down for every variable that is actually specified. I'm
    * aware that this is by no means the most efficient way of going about this, but my advisor suggested that
    * I do it this way due to the fact that a) the number of match instances is in the hundreds, so that sort
    * of algorithmic efficiency is hardly noticeable, and b) the alternative would be manually generating SQL
    * calls, which is just too much of a pain. I have yet to implement this function, so I'm not certain that
    * it works.*/
    def specify = {
        def matchInstance = new Match()
        def matches = Match.list()
        //BEGIN TEMPORARY FUNCTIONALITY--only for presentation purposes
        if (params.sPlayer1.id && params.sPlayer2.id){//could also forward this to PlayerController
            redirect(action: "pvp", params: [player1ID: params.sPlayer1.id, player2ID: params.sPlayer2.id])
        }
        else if (params.sPlayer1.id){
            forward(controller: "player", action: "show", id: params.sPlayer1.id)
        }
        //END TEMPORARY FUNCTIONALITY
        if (params.sPlayer1.id){
            def player = Player.findById(params.sPlayer1.id)
            def playerMatches = []
            for (ms in player.matches){
                playerMatches.add(ms.match)
            }
            matches = matches.intersect(playerMatches)
        }
        if (params.sChar1.id){
            def character = Fighter.findById(params.sChar1.id)
            def charMatches = []
            for (ms in Matchship.findAllByCharacter(character)){
                charMatches.add(ms.match)
            }
            matches = matches.intersect(charMatches)
        }
        if (params.sPlayer2.id){
            def player = Player.findById(params.sPlayer2.id)
            def playerMatches = []
            for (ms in player.matches){
                playerMatches.add(ms.match)
            }
            matches = matches.intersect(playerMatches)
        }
        if (params.sChar2.id){
            def character = Fighter.findById(params.sChar2.id)
            def charMatches = []
            for (ms in Matchship.findAllByCharacter(character)){
                charMatches.add(ms.match)
            }
            matches = matches.intersect(charMatches)
        }
        if (params.sStage.id){
            def stage = Stage.findById(params.sStage.id)
            matches = matches.intersect(Match.findAllByStage(stage))
        }
        if (params.sTournament.id){
            def tourn = Tournament.findById(params.sTournament.id)
            matches = matches.intersect(Match.findAllByTournament(tourn))
        }
        if (params.sBracket.id){
            def bracket = Bracket.findById(params.sBracket.id)
            matches = matches.intersect(Match.findAllByBracket(bracket))
        }
        return [matches:matches]
    }

    def pvp = {
        print(params)
        def player1 = Player.findById(params.player1ID)
        def player2 = Player.findById(params.player2ID)
        def matches = player1.matches.match
        matches = matches.intersect(player2.matches.match)
        return[matches: matches, player1: player1, player2: player2]
    }
}
